/*
 * Copyright (c) 2011-2012 Alexander Dubu
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * o  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * o  Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * o  Neither the name Axil nor the names of its contributors may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package axil.engine.access.transform;

import static axil.framework.Functions.*;


/**
 * A transform for rewriting Java field and method names into Axil identifiers.
 * The identifier is broken apart and transformed into a formal title. For
 * example, a method such as getShoeSize() is transformed into the Axil
 * identifier "Shoe-Size". Likewise, a field such as "shoeSize" is transformed
 * into "Shoe-Size".
 */
class Dashes extends Transform {
	private static final int
	SKIP = 1,
	START = 2,
	WORD = 3,
	ACRONYM = 4,
	CAPS = 5;

	/**
	 * Transform the given Java field name or method name into an Axil
	 * identifier.
	 */
	protected String transform(String name) {
		int i = 0;
		final int size = name.length();
		StringBuilder word = new StringBuilder();
		StringBuilder b = new StringBuilder(size + 5);
		int state = SKIP;
		while(i < size) {
			char ch = name.charAt(i);
			switch (state) {
			case SKIP:
				if (! skip(ch)) {
					i--;
					state = START;
				}
				break;

			case START:
				if (Character.isUpperCase(ch)) {
					word.append(Character.toLowerCase(ch));
					state = CAPS;
				} else {
					word.append(ch);
					state = WORD;
				}
				break;

			case CAPS:
				word.append(Character.toLowerCase(ch));
				state = Character.isUpperCase(ch) ? ACRONYM : WORD;
				break;

			case ACRONYM:
				if (Character.isUpperCase(ch)) {
					word.append(Character.toLowerCase(ch));
				} else {
					// Something like XLMen --> XL Men
					char saved = word.charAt(word.length() - 1);
					word.setLength(word.length() - 1);
					put(b, word);
					word.append(saved);
					word.append(ch);
					state = CAPS;
				}
				break;

			case WORD:
				if (Character.isUpperCase(ch)) {
					put(b, word);
					i--;
					state = START;
				} else {
					if (skip(ch)) {
						put(b, word);
						state = START;
					} else {
						word.append(ch);
					}
				}
				break;

			default:
				throw unreachable();
			}
			i++;
		}
		return check(name, b, word);
	}


	private boolean skip(char ch) {
		return (ch == '_') || (ch == '$');
	}


	private void put(StringBuilder b, StringBuilder word) {
		if (b.length() > 0) {
			b.append('-');
		}
		b.append(word);
		word.setLength(0);
	}


	private String check(String name, StringBuilder b, StringBuilder word) {
		if (word.length() > 0) {
			put(b, word);
		}
		if (b.length() == 0) {
			throw abort(
			    "The Java identifier cannot be transformed into an Axil " +
			    "identifier.", nv("java", name));
		}
		if (Character.isDigit(b.charAt(0))) {
			return "A " + b;
		}
		return b.toString();
	}
}
